/**
 * 
 */
package idv.takeshi.software.productline.bookstore.utils.template;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * A template generator utilities.
 * @author takeshi
 *
 */
public class TemplateUtils {
	
	private static final Log log = LogFactory.getLog(TemplateUtils.class);
	
	/**
	 * Used for default encoding.
	 */
	private static String defaultEncoding = "UTF-8";
	
	/**
	 * A cacher for caching the Configuration Object and it's corresponding templatePath.<br>
	 * Map, key => String templatePath, Configuration config Object.
	 */
	private static final Map<String, Configuration> CONFIGS_CACHER = new HashMap<String, Configuration>();
	
	/**
	 * Generate a string based on the given parameters. 
	 * @param templatePath the path of the template files located.
	 * @param templateFileName the template file name, with file extension suffixed.
	 * @param variablesHolder variable name as a key, and variable value as a value for the given Map.
	 * @param locale i18N use.
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static String generateTemplateString(
			String templatePath, String templateFileName, Map variablesHolder, Locale locale) {
		Validate.notEmpty(templatePath, "templatePath shall always not be null/empty");
		Validate.notEmpty(templateFileName, "templateFileName shall always not be null/empty");
		Validate.notEmpty(variablesHolder, "variablesHolder shall always not be null/empty");
		
		String templateString = null;
		Map useVariablesHloder = null; 
		
		if(null == variablesHolder){
			useVariablesHloder = new HashMap();
		} else{
			useVariablesHloder = variablesHolder;
		}
		
		Configuration config = getConfiguration(templatePath);
		Template temp = null;
		Writer writer = null;
		File templateStringFile = null;
		try {
			if(null == locale){
				temp = config.getTemplate(templateFileName);
			} else{
				temp = config.getTemplate(templateFileName, locale);
			}
			templateStringFile = File.createTempFile(UUID.randomUUID().toString(), null);
			writer = new PrintWriter(templateStringFile);
			temp.process(useVariablesHloder, writer);
		} catch (Exception e) {
			String msg = "template generation failed";
			log.error(msg, e);
			throw new TemplateGenerationFailedException(msg, e);
		} finally{
			IOUtils.closeQuietly(writer);
		}
		
		try {
			templateString = FileUtils.readFileToString(templateStringFile);
		} catch (IOException e) {
			String msg = "read generated template file content failed";
			log.error(msg, e);
			throw new TemplateGenerationFailedException(msg, e);
		}
		
		return templateString;
	}
	
	private static Configuration getConfiguration(String templatePath) {
		Configuration config = null;
		if(getConfigsCacher().containsKey(templatePath)){
			config = getConfigsCacher().get(templatePath);
		} else{
			try{
			URL url = null;
			File file = null;
			url = TemplateUtils.class.getResource(templatePath);
			
			file = new File(url.toURI());
			
			config = new Configuration();
	        config.setDirectoryForTemplateLoading(file);
	        config.setObjectWrapper(new DefaultObjectWrapper());
	        config.setDefaultEncoding(defaultEncoding);
			} catch(Exception e){
				String msg = "create configuration failed";
				log.error(msg, e);
				throw new TemplateGenerationFailedException(msg, e);
			}
			getConfigsCacher().put(templatePath, config);
		}
		
		return config;
	}

	/**
	 * @return the configsCacher
	 */
	protected static Map<String, Configuration> getConfigsCacher() {
		return CONFIGS_CACHER;
	}

	
}
